#include "uhm.hxx"

#include "uhm/wrapper/fort/util.hxx"
#include "uhm/wrapper/fort/common.hxx"
#include "uhm/wrapper/fort/linal.hxx"

using namespace uhm;

#ifndef __ID_FORT__
#define __ID_FORT__ 1
#endif 

#define N __ID_FORT__

typedef ID_Node_H_<N>          Id_Nd;
typedef ID_Element_<1>         Id_El;

// -- Methods -----------------------------------------------------------------------
F_void UHM_C2F(uhm_solver_petsc_interfaced)(F_ptr *solver, F_int *flag) {
  *flag = ( (solver->second >=  UHM_FORT_PETSC_BEGIN) &&
            (solver->second <   UHM_FORT_PETSC_END) );
}

// Direct Methods
F_void UHM_C2F(uhm_direct_solver_decompose)(F_ptr *solver) {
  Direct_Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->decompose();
}

F_void UHM_C2F(uhm_direct_solver_updecompose)(F_ptr *solver) {
  Direct_Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->updecompose();
}

F_void UHM_C2F(uhm_direct_solver_solve)(F_ptr *solver) {
  Direct_Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->solve();
}

F_void UHM_C2F(uhm_direct_solver_check)(F_ptr *solver, F_double *residual) {
  Direct_Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->check(*residual);
}

// Solver Common Methods
F_void UHM_C2F(uhm_solver_finalize)(F_ptr *solver) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  delete s; 
}

F_void UHM_C2F(uhm_solver_content)(F_ptr *solver) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->content(std::cout);
}

F_void UHM_C2F(uhm_solver_report)(F_ptr *solver) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->report(std::cout);
}

F_void UHM_C2F(uhm_solver_create)(F_ptr *solver) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  UHM_ERROR(s->create(), ">> Error in Solver Creating (fortran)");
}

F_void UHM_C2F(uhm_solver_copy_in_begin)(F_ptr *solver) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  UHM_ERROR(s->copy_in_begin(), ">> Error in Solver Copy in Begin (fortran)");
}

F_void UHM_C2F(uhm_solver_copy_in_end)(F_ptr *solver) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  UHM_ERROR(s->copy_in_end(), ">> Error in Solver Copy in End (fortran)");
}

F_void UHM_C2F(uhm_solver_copy_in_ab)(F_ptr *solver,
                                      F_int *el, F_int *eids, 
                                      F_int *nodes, F_int *nids, 
                                      F_int *dofs, F_int *weights, F_int *kinds, F_int *n_nodes,
                                      F_int *cs, F_void *buf_A, F_void *buf_B) {
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  UHM_ERROR(s->copy_in(el, *eids, nodes, *nids, dofs, weights, kinds, *n_nodes, *cs, buf_A, buf_B),
            ">> Error in Solver Copy In (fortran)");
}

F_void UHM_C2F(uhm_solver_copy_in_b)(F_ptr *solver,
                                     F_int *nodes, F_int *ids,
                                     F_int *dofs, F_int *n_nodes,
                                     F_int *cs, F_void *buf_B) {
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->copy_in(nodes, *ids, dofs, *n_nodes, *cs, buf_B);
}

F_void UHM_C2F(uhm_solver_copy_out_el)(F_ptr *solver, 
                                       F_int *el, F_int *ids,
                                       F_int *cs, F_void *X) {
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  UHM_ERROR(s->copy_out(el, *ids, *cs, X), ">> Error in Solver Copy Out (fortran)");
}

F_void UHM_C2F(uhm_solver_copy_out_nodes)(F_ptr *solver, 
                                          F_int *nodes, F_int *ids, F_int *n_nodes,
                                          F_int *cs, F_void *X) {
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  UHM_ERROR(s->copy_out(nodes, *ids, *n_nodes, *cs, X), ">> Error in Solver Copy Out (fortran)");
}

F_void UHM_C2F(uhm_solver_set_caching)(F_ptr *solver, F_int *caching) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->set_caching(*caching);
}
F_void UHM_C2F(uhm_solver_get_caching)(F_ptr *solver, F_int *caching) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  *caching = s->get_caching();
}

F_void UHM_C2F(uhm_solver_set_level)(F_ptr *solver, F_int  *level) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  s->set_level(*level);
}
F_void UHM_C2F(uhm_solver_get_level)(F_ptr *solver, F_int  *level) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  *level = s->get_level();
}

F_void UHM_C2F(uhm_solver_get_rhs)(F_ptr *solver, F_int  *rhs) { 
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  *rhs = s->get_rhs();
}

F_void UHM_C2F(uhm_solver_clear)(F_ptr *solver) {
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  UHM_ERROR(s->clear(), ">> Error in Solver Flushing (fortran)");
}

F_void UHM_C2F(uhm_solver_flush)(F_ptr *solver) {
  Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PTR(s,Id_Nd,Id_El,solver);
  UHM_ERROR(s->flush(), ">> Error in Solver Flushing (fortran)");
}


